import type { DragElementConfig } from '../../../../../../lib/drag_handler'
import type {
  AdResultSelectors,
  SearchAdBadgeOptions,
  SearchPageAdBadgeOptions,
  SearchPageTitleCleanupConfig,
  SearchSelectorConfig,
} from '../index'
import type {
  PageSearchMetaNormalConfig,
  PageSearchMetaSourceConfig,
  PageSearchMetaTimeConfig,
  SearchMetaTypeConfig,
} from './index'
import { StyleModule } from '../../../../../../lib/abstract'
import { DragHandler } from '../../../../../../lib/drag_handler'
import { PageHandler } from '../../../../../../lib/page_handler'
import {
  SO_SEARCH_PAGE_SELECTORS,
  SoPageType,
} from '../../../../../../lib/page_handler/platform/so/types'
import { ToolKit } from '../../../../../../lib/utils/toolkit'
import {
  AppStyleCollections,
  BodyAttrs,
  HOSTNAME,
  LOCAL_STORAGE_KEYS,
  logger,
  SUPPORTED_PLATFORMS,
} from '../../../../../../types/constants'
import {
  SearchMetaLeftType,
  SearchMetaRightType,
  SearchStyle,
} from './index'

export class SoStyle extends StyleModule {
  readonly name: string = 'SoStyle'
  private static instance: SoStyle
  private searchStyle: SearchStyle = SearchStyle.getInstance()
  private pageHandler: PageHandler = PageHandler.getInstance()
  private readonly pageType = this.pageHandler.pages.so.pageType
  private readonly selectors: SearchSelectorConfig = this.pageHandler.pages.so.getResultSelectors()
  private webObserver: MutationObserver | null = null
  private loadingObserver: MutationObserver | null = null

  constructor() {
    super()

    if (HOSTNAME !== SUPPORTED_PLATFORMS.SO)
      return

    this.searchStyle.executeInit(
      this.selectors.selector,
      [
        SoPageType.Video,
        SoPageType.Wenku,
      ],
      () => {
        this.init()
        this.startLoadingObserver()
      },
    )
  }

  static getInstance(): SoStyle {
    if (!SoStyle.instance) {
      SoStyle.instance = new SoStyle()
    }
    return SoStyle.instance
  }

  /**
   * 初始化
   */
  init(): void {
    this.initWebObserver()
    this.initResultMeta()
    this.updateSearchStyle()
    this.addAdBadge()
    this.updateTitle()
    this.initSidebarDrag()
  }

  /**
   * 初始化网页观察器
   */
  private initWebObserver() {
    if (this.pageType !== SoPageType.Web)
      return

    // 如果已经存在观察器，先断开
    this.stopObservation()

    // 遮罩观察器
    this.webObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          if (Array.from(mutation.addedNodes).some(n => (n as Element).tagName === 'IFRAME' && !(n as Element).id)) {
            // 由于是同步添加节点，所以网页内容是完全加载的
            logger.attention(`[${this.name}] 网页触发更新`)
            this.handleWebUpdate()
          }
        }

        if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
          Array.from(mutation.removedNodes).forEach((n) => {
            if ((n as Element).getAttribute(BodyAttrs.PROJECT_MODULE)) {
              if (!document.body.contains(n)) {
                document.body.appendChild(n)
              }
            }
          })
        }
      })
    })

    // 监听变化
    this.webObserver.observe(document.body, {
      childList: true, // 只需要直接子元素变化
      subtree: false,
    })
  }

  /**
   * 观察搜索加载元素的显示状态变化
   */
  private startLoadingObserver() {
    if (this.pageType !== SoPageType.Video)
      return

    this.stopObservation()

    // 创建观察
    this.loadingObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          if (Array.from(mutation.addedNodes).some(n => (n as Element).id.endsWith('video-result_frame'))) {
            logger.attention(`[${this.name}] 搜索结果已更新`)
            this.handleNewResultLoaded()
          }
        }
      })
    })

    this.loadingObserver.observe(document.body, {
      childList: true,
      subtree: false,
    })
  }

  /**
   * 停止所有观察器
   */
  stopObservation(): void {
    switch (this.pageType) {
      case SoPageType.Web:
        this.webObserver?.disconnect()
        this.webObserver = null
        break
      case SoPageType.Video:
      case SoPageType.Wenku:
        this.loadingObserver?.disconnect()
        this.loadingObserver = null
        break
    }
  }

  /**
   * 处理网页更新
   */
  handleWebUpdate(): void {
    this.hideElements()
    this.initResultMeta()
    this.updateSearchStyle()
  }

  /**
   * 处理新结果加载完成
   */
  handleNewResultLoaded(): void {
    ToolKit.advanced.performance.doubleRAF(() => {
      this.hideElements()
      this.initResultMeta()
      this.updateSearchStyle()
    })
  }

  /**
   * 初始化搜索样式
   */
  updateSearchStyle(forceUpdate?: boolean): void {
    const supportPages = [
      SoPageType.Web,
      SoPageType.News,
      SoPageType.Encyclopedia,
      SoPageType.Wenku,
    ]

    this.searchStyle.setBaseStyle(this.selectors, { supportPages, forceUpdate })
  }

  addAdBadge(): void {
    const pageType = PageHandler.getCurrentPageType() as SoPageType
    const defaultConfig = {
      onPrepare: (config: AdResultSelectors): void => {
        $(config.itemTitle || '').each((_, el) => {
          const $title = $(el)

          // 包裹文本内容
          $title
            .filter('.e_haosou_fw_bg_title')
            .contents()
            .wrapAll('<div class="title-wrapper"></div>')
        })
      },
      config: (SO_SEARCH_PAGE_SELECTORS[pageType]?.adResult || {}) as AdResultSelectors,
    }
    const pageConfig: SearchPageAdBadgeOptions = {
      [SoPageType.Web]: {
        ...defaultConfig,
      },
      [SoPageType.Wenku]: {
        ...defaultConfig,
      },
    }

    const currentConfig = (pageConfig[pageType] || {}) as SearchAdBadgeOptions

    if (Object.keys(currentConfig).length === 0) {
      return
    }

    const { onPrepare, config = {} as AdResultSelectors } = currentConfig

    this.searchStyle.addAdBadge({ onPrepare, config })
  }

  /**
   * 初始化结果元数据
   */
  initResultMeta(): void {
    const typeConfig: SearchMetaTypeConfig = {
      [SoPageType.Wenku]: {
        leftType: SearchMetaLeftType.Normal,
        rightType: SearchMetaRightType.Normal,
      },
    }

    const { leftType = SearchMetaLeftType.Source, rightType = SearchMetaRightType.Time } = typeConfig[this.pageType] || {}

    const baseSourceConfig = {
      base: {
        selector: '.g-linkinfo cite a, .g-linkinfo cite:not(:has(a)), #mohe-zz_medicine_jd .mh-src, .g-mohe-radius:first-child .url, #mohe-biu_train .mh-site, #mohe-plane_new .mh-footer, .e_haoso_fengwu_extend cite > a',
        container: '.g-linkinfo, #mohe-zz_medicine_jd .mh-src, #mohe-biu_train .mh-site, #mohe-plane_new .mh-footer, .e_haoso_fengwu_extend',
      },
    }

    const sourceConfig: PageSearchMetaSourceConfig = {
      [SoPageType.Web]: baseSourceConfig,
      [SoPageType.Video]: {
        base: {
          selector: '.v-item__extra .v-item__extra-name',
          container: '.v-item__extra',
          enableIcon: true,
        },
      },
      [SoPageType.News]: {
        base: {
          selector: '.sitename',
          container: '.g-linkinfo',
        },
      },
      [SoPageType.Encyclopedia]: {
        base: {
          selector: '.res-desc cite',
          container: '.res-desc cite',
        },
      },
    }

    const timeConfig: PageSearchMetaTimeConfig = {
      [SoPageType.Web]: {
        selector: '.res-desc span[class], .res-rich-wenda_box p:contains(提问时间)',
        cleanFunc: (text: string) => text
          .replace(/^-/, '')
          .replace(/&nbsp;/g, '')
          .trim(),
      },
      [SoPageType.Video]: {
        selector: '.v-item__extra span:last-child',
      },
      [SoPageType.News]: {
        selector: '.g-linkinfo-txt',
      },
      [SoPageType.Encyclopedia]: {
        selector: '.res-desc cite',
        cleanFunc: (text: string) => (text.split(/\s+/)?.[1] || '').trim(),
      },
    }

    const leftNormalConfig: PageSearchMetaNormalConfig = {
      [SoPageType.Wenku]: {
        selector: '.wenku-search__resList__item__info__other span:first-child, .e_haoso_fengwu_extend cite a',
      },
    }
    const rightNormalConfig: PageSearchMetaNormalConfig = {
      [SoPageType.Wenku]: {
        selector: '.wenku-search__resList__item__info__other span:last-child, .e_haoso_fengwu_extend > a',
      },
    }

    this.searchStyle.createResultMeta(
      this.selectors.selector,
      {
        leftType,
        rightType,
        leftConfig: leftType === SearchMetaLeftType.Source ? sourceConfig : leftNormalConfig,
        rightConfig: rightType === SearchMetaRightType.Time ? timeConfig : rightNormalConfig,
      },
    )
  }

  /**
   * 更新标题
   */
  updateTitle(): void {
    const supportPages = [
      SoPageType.Encyclopedia,
    ]

    if (!supportPages.includes(this.pageType as SoPageType))
      return

    const config: SearchPageTitleCleanupConfig = {
      [SoPageType.Encyclopedia]: {
        platformName: '360百科',
      },
    }

    this.searchStyle.updateTitle({
      config,
      container: this.selectors.selector,
      titleSelector: 'h3 a',
      highlightKeywords: ToolKit.browser.url.getUrlParams('q'),
    })
  }

  private initSidebarDrag(): void {
    const supportPages = [
      SoPageType.Web,
      SoPageType.News,
      SoPageType.Encyclopedia,
    ]

    if (!supportPages.includes(this.pageType as SoPageType))
      return

    const asideConfig = (SO_SEARCH_PAGE_SELECTORS[this.pageType as SoPageType]?.rightAside || {}) as DragElementConfig
    const { containerSelector = '' } = asideConfig

    const dragHandler = new DragHandler({
      onPrepare: () => {
        const supportPages = [
          SoPageType.Web,
          SoPageType.Encyclopedia,
        ]

        if (!supportPages.includes(this.pageType as SoPageType))
          return true

        let targetSelector: string = ''

        if (this.pageType as SoPageType === SoPageType.Web) {
          targetSelector = `${containerSelector} #mohe-know_side_nlp-imagelist`
        }
        else if (this.pageType as SoPageType === SoPageType.Encyclopedia) {
          targetSelector = containerSelector
        }

        const $container = $(containerSelector)

        if (!$container.length) {
          logger.error(`[${this.name}] 未找到侧边栏容器: ${containerSelector}`)
          return false
        }
        else {
          logger.info(`[${this.name}] 侧边栏容器存在: ${containerSelector}`)
        }

        const $target = $(targetSelector)

        if (!$target.length) {
          logger.error(`[${this.name}] 未找到挂载容器: ${targetSelector}`)
          return false
        }

        $target.prepend(`<div class="${AppStyleCollections.Drag.AREA}"></div>`)
        return true
      },
      elementConfig: asideConfig,
      storageKey: `${LOCAL_STORAGE_KEYS.Sidebar}-${this.pageType}`,
    })
    dragHandler.init()
  }
}
